#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <stdarg.h>
#include <arpa/inet.h>


#define MAXLINE    100


void print_family(struct addrinfo *aip) ;
void print_type(struct addrinfo *aip);
void print_protocol(struct addrinfo *aip);
void print_flags(struct addrinfo *aip);


void err_doit(int errnoflag, int error, const char *fmt, va_list ap) {
    char buf[MAXLINE];

    vsnprintf(buf, MAXLINE - 1, fmt, ap);
    if (errnoflag)
        snprintf(buf + strlen(buf), MAXLINE - strlen(buf) - 1, ": %s", strerror(error));
    strcat(buf, "\n");
    fflush(stdout);
    fputs(buf, stderr);
    fflush(NULL); // flushes all stdio output streams
}

void err_quit(const char *fmt, ...) {
    va_list ap;

    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
    exit(1);
}

/**
 * 运行方式: $ ./a.out harry nfs
 */
int main(int argc, char *argv[]) {
    struct addrinfo *ailist, *aip;
    struct addrinfo hint;
    struct sockaddr_in *sinp;
    const char *addr;
    int err;
    char abuf[INET_ADDRSTRLEN];

    if (argc != 3)
        err_quit("usage: %s nodename service", argv[0]);
    else 
        printf("ready to run: %s %s %s\n", argv[0], argv[1], argv[2]);

    memset(&hint, 0, sizeof(hint));
    hint.ai_flags = AI_CANONNAME;
    hint.ai_family = 0;
    hint.ai_socktype = 0;
    hint.ai_protocol = 0;
    hint.ai_addrlen = 0;
    hint.ai_canonname = NULL;
    hint.ai_addr = NULL;
    hint.ai_next = NULL;
    
    if ((err == getaddrinfo(argv[1], argv[2], &hint, &ailist)) != 0) 
        err_quit("getaddrinfo error: %s", gai_strerror(err));

    printf("sucess to call getaddrinfo\n");
    int count = 0;

    for (aip = ailist; aip != NULL; aip = aip->ai_next) {
        count++;
        print_flags(aip);
        print_family(aip);
        print_type(aip);
        print_protocol(aip);
        printf("\n\thost %s", aip->ai_canonname? aip->ai_canonname: "-");

        if (aip->ai_family == AF_INET) {
            sinp = (struct sockaddr_in *)aip->ai_addr;
            addr = inet_ntop(AF_INET, &sinp->sin_addr, abuf, INET_ADDRSTRLEN);
            printf(" address %s", addr? addr:"unknown");
            printf(" port %d", ntohs(sinp->sin_port));
        }

        printf("\n");
    }
    freeaddrinfo(ailist);

    printf("count = %d\n", count);

    return 0;
}


void print_family(struct addrinfo *aip) {
    printf(" family ");
    switch(aip->ai_family) {
    case AF_INET:
        printf("inet");
        break;
    case AF_INET6:
        printf("inet6");
        break;
    case AF_UNIX:
        printf("unix");
        break;
    case AF_UNSPEC:
        printf("unspecified");
        break;
    default:
        printf("unknown");
    }
}

void print_type(struct addrinfo *aip) {
    printf(" type ");
    switch(aip->ai_socktype) {
    case SOCK_STREAM:
        printf("stream");
        break;
    case SOCK_DGRAM:
        printf("datagram");
        break;
    case SOCK_SEQPACKET:
        printf("seqpacket");
        break;
    case SOCK_RAW:
        printf("raw");
        break;
    default:
        printf("unknown (%d)", aip->ai_socktype);
    }
}

void print_protocol(struct addrinfo *aip) {
    printf(" protocol ");
    switch(aip->ai_protocol) {
    case 0:
        printf("default");
        break;
    case IPPROTO_TCP:
        printf("TCP");
        break;
    case IPPROTO_UDP:
        printf("UDP");
        break;
    case IPPROTO_RAW:
        printf("raw");
        break;
    default:
        printf("unknow (%d)", aip->ai_protocol);
    }
}

void print_flags(struct addrinfo *aip) {
    printf(" flags ");
    int flags = aip->ai_flags;

    if(flags == 0) {
        printf("0");
    }
    else {
        if (flags & AI_PASSIVE)
            printf("passive");
        if (flags & AI_CANONNAME)
            printf("cannon");
        if (flags & AI_NUMERICHOST)
            printf("numhost");
        if (flags & AI_NUMERICSERV)
            printf("numserv");
        if (flags & AI_V4MAPPED)
            printf("v4mapped");
        if (flags & AI_ALL)
            printf("all");
    }
}
